package server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.List;

import girafPlace.lib.Application;
import girafPlace.lib.UserProfile;
import girafPlace.lib.Version;

/**
 * The ConnectionThread handles a single client connection. It will ensure that
 * both client and server uses the same version before allowing the client be
 * download the list of applications for a provided user profile.
 * 
 * @author sw6c
 * 
 */
public class ConnectionThread extends Thread {
	private boolean done = false;
	private boolean connected = false;

	private Socket socket;
	private ObjectInputStream in;
	private ObjectOutputStream out;

	/** Used for identifying a thread in the debug output */
	private final int threadNumber;

	/**
	 * Creates a new ConnectionThread object.
	 * 
	 * @param socket
	 *            A socket connected to an Android client socket.
	 * @param threadNumber
	 *            The thread number, used for debugging.
	 * @throws IOException
	 *             If there is a problem with a socket.
	 */
	public ConnectionThread(Socket socket, int threadNumber) throws IOException {
		this.socket = socket;
		this.threadNumber = threadNumber;

		output("Starting thread with client " + socket.getRemoteSocketAddress());

		// Output stream must be opened and flushed before the inputstream
		out = new ObjectOutputStream(socket.getOutputStream());
		out.flush();
		output("Initiated Out");
		in = new ObjectInputStream(socket.getInputStream());
		output("Initiated In");

	}

	/**
	 * Main logic function for the thread. Will handle a client connection
	 * according to the definition of the GTP. In the case of any exception, it
	 * will be printed, and the loop will end.
	 */
	public void run() {
		while (!done) {
			try {
				String input = in.readUTF();

				if (input == null) {
					done = true;
					output("Recieved null, ending connection");
					socket.close();
				} else if (input.compareTo("HANDSHAKE") == 0) {
					int version = Integer.parseInt((String) in.readUTF());

					if (version != Version.Version) {
						out.writeUTF("OLDVERSION");
						out.flush();
						output("The version did not correspond with the current build.");
						return;
					} else {
						connected = true;
						out.writeUTF("WELCOME");
						out.flush();
						output("The client version was correct, responded with Welcome");
					}
				} else if (input.compareTo("GETAPPS") == 0 && connected) {
					List<Application> applications;
					UserProfile profile = (UserProfile) in.readObject();

					try {
						applications = SqlManager.getApplications(profile);
						output(String.format(
								"Writing %1$d applications into the stream",
								applications.size()));
						out.writeObject(applications);
						out.flush();
					} catch (Exception e) {
						e.printStackTrace();
					}
				} else if (input.compareTo("SIGNOUT") == 0 && connected) {
					out.writeUTF("KTHXBYE");
					out.flush();
					done = true;
					socket.close();
					output("The client disconnected");
				} else {
					output(String.format("The server recived %1$s", input));
				}
			} catch (Exception e) {
				output(e.getMessage());
				done = true;
			}
		}
	}

	/**
	 * Convenience method for printing a line to the console, prefixed with the
	 * thread number.
	 * 
	 * @param output
	 *            The string to be printed.
	 */
	protected void output(String output) {
		System.out
				.println(String
						.format(
								"[%1$d] %2$s. The connection variable is: %3$b, and the number of active threads is: %4$d",
								threadNumber, output, connected, Thread
										.activeCount()));
	}
}
